package cn.dduan.fool.volatil;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Volatile
 * volatile不保证原子性的解决方法
 * 1、使用synchronized
 * 2、AtomicInteger
 * 应用场景：多个线程读，一个线程写的场合，例如购票系统
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/6
 */
public class MyVolatile {
    // 无可见性
    // int num = 0;

    // 保证可见性
    volatile int num = 0;

    // 2、AtomicInteger 保证原子性
    volatile AtomicInteger n = new AtomicInteger();

    public void add1() {
        num++;
    }

    /**
     * 1、synchronized 保证原子性
     *
     * @param
     * @return void
     * @author dduan
     * @version 1.0
     * @date 2023/4/6
     */
    public synchronized void add2() {
        num++;
    }

    /**
     * 2、AtomicInteger 保证原子性
     *
     * @param
     * @return void
     * @author dduan
     * @version 1.0
     * @date 2023/4/6
     */
    public void add3() {
        num++;
        n.getAndIncrement();
    }



    /**
     * 测试可见性
     *
     * @param
     * @return void
     * @author dduan
     * @version 1.0
     * @date 2023/4/6
     */
    private void testVolatile() {
        MyVolatile m = new MyVolatile();
        // 启动一个线程修改num的值
        new Thread(
                () -> {
                    System.out.println("线程" + Thread.currentThread().getName()+"\t 正在执行");
                    try{
                        TimeUnit.SECONDS.sleep(3);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    m.add1();
                    System.out.println("线程" + Thread.currentThread().getName()+"\t 更新后，number的值为" + m.num);
                }
        ).start();

        // 检查主线程是否保持可见性
        while (m.num == 0){
            // 当上面的线程将num加1后，如果有可见性的话，那么就会跳出循环；
            // 如果没有可见性的话，就会一直在循环里执行
        }
        System.out.println("具有可见性！");
    }
    
    /**
     * 不保证原子性的代码验证
     * 
     * @param 
     * @return void
     * @author dduan
     * @version 1.0
     * @date 2023/4/6
     */
    private void testAtom() {
        MyVolatile m = new MyVolatile();
        // 启动20个线程，每个线程将MyVolatile的num值加1000次，那么理论上num值最终是20000
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    m.add3();
                }
            }).start();
        }

        // 程序运行时，模型会有主线程和守护线程。如果超过２个，那就说明上面的２０个线程还有没执行完的，就需要等待
        while (Thread.activeCount()>2){
            Thread.yield();
        }

        System.out.println("num值加了20000次，此时num的实际值是：" + m.num);
        System.out.println("n值加了20000次，此时n的实际值是：" + m.n);
    }

    public static void main(String[] args) {

        MyVolatile m = new MyVolatile();
        // 测试可见性
        // m.testVolatile();
        // 不保证原子性的代码验证
        m.testAtom();
    }
}
